package matrix;

import java.util.Arrays;

public class Matrix implements Cloneable {

	private Number[][] matrix;
	private int type;

	public Matrix(int w, int h, int type) {
		matrix = new Number[h][w];
		this.type = type;
		for (int i = 0; i < matrix.length; i++) {
			Arrays.fill(matrix[i], 0);
		}
	}

	public Matrix(String[][] init, int type) {
		this.type = type;
		matrix = new Number[init.length][init[0].length];
		Utils.fill(matrix, init, type);
	}

	public static Matrix Identity(int n, int type) {
		Matrix out = new Matrix(n, n, type);
		for (int i = 0; i < n; i++)
			out.set(i, i, 1);
		return out;
	}

	public void set(int x, int y, Number n) {
		matrix[y][x] = Utils.convert(n, type);
	}

	public Number get(int x, int y) {
		return matrix[y][x];
	}

	public Matrix subtract(Matrix m) {
		Matrix out = new Matrix(width(), height(), type);
		for (int i = 0; i < height(); i++)
			for (int j = 0; j < width(); j++)
				out.set(j, i, matrix[i][j].doubleValue() - m.matrix[i][j].doubleValue());
		return out;
	}

	public Matrix multiply(Matrix m) {
		Matrix out = new Matrix(m.width(), height(), type);
		for (int y = 0; y < height(); y++)
			for (int x = 0; x < m.width(); x++)
				for (int i = 0; i < height(); i++)
					out.set(x, y, out.matrix[y][x].doubleValue() + matrix[y][i].doubleValue() * m.matrix[i][x].doubleValue());
		return out;
	}

	public Matrix augment(Matrix m) {
		Matrix out = new Matrix(width() + m.width(), height(), type);
		for (int y = 0; y < height(); y++)
			for (int x = 0; x < width(); x++)
				out.set(x, y, matrix[y][x]);
		for (int y = 0; y < m.height(); y++)
			for (int x = 0; x < m.width(); x++)
				out.set(x + width(), y, m.matrix[y][x]);
		return out;
	}

	public Matrix swapRow(int i, int j) {
		Number[] t = matrix[i];
		matrix[i] = matrix[j];
		matrix[j] = t;
		return this;
	}

	public int pivot(int x, int y) {
		int row = y;
		Number n = matrix[y++][x];
		for (; y < height(); y++) {
			if (Utils.compare(Utils.abs(matrix[y][x], type), n) > 0) {
				n = matrix[y][x];
				row = y;
			}
		}
		return row;
	}

	public Number solveRows(int r1, int r2, int x) {
		if (matrix[r2][x].doubleValue() == 0)
			return 0;
		if (matrix[r1][x].doubleValue() == 0) {
			swapRow(r1, r2);
			return solveRows(r1, r2, x);
		}
		double m = matrix[r2][x].doubleValue() / matrix[r1][x].doubleValue();
		for (int i = 0; i < width(); i++)
			set(i, r2, matrix[r2][i].doubleValue() - matrix[r1][i].doubleValue() * m);
		return m;
	}

	@Override
	protected Matrix clone() {
		Matrix m = new Matrix(width(), height(), type);
		for (int i = 0; i < height(); i++)
			for (int j = 0; j < width(); j++)
				m.set(j, i, matrix[i][j].doubleValue());
		return m;
	}

	public int height() {
		return matrix.length;
	}

	public int width() {
		return matrix[0].length;
	}

	public int type() {
		return type;
	}

	public String toLatex(int[] w) {
		// begin
		String out = "\\left[\\begin{array}{";
		// format
		for (int i = 0; i < w.length; i++) {
			for (int j = 0; j < w[i]; j++)
				out += "c";
			out += "|";
		}
		out += "c}";
		// matrix
		for (int y = 0; y < matrix.length; y++) {
			for (int x = 0; x < matrix[y].length; x++)
				out += matrix[y][x] + "&";
			out = out.substring(0, out.length() - 1) + "\\\\";
		}
		out = out.substring(0, out.length() - 2);
		// end
		out += "\\end{array}\\right]";
		return out;
	}

	public String toLatex() {
		return toLatex(new int[] {});
	}

	@Override
	public String toString() {
		String out = "";
		for (int i = 0; i < matrix.length; i++) {
			out += "|";
			for (int j = 0; j < matrix[i].length; j++) {
				out += "\t" + matrix[i][j];
			}
			out += "|\n";
		}
		return out;
	}

}
